﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;

namespace Wizard.UI
{
    public partial class WizardSheet : Form
    {
        private IList p_Pages = new ArrayList();
        private WizardPage p_ActivePage = null;

        public WizardSheet()
        {
            InitializeComponent();
        }

        /// <summary>
        /// A list of pages that the wizard control will display in order.
        /// </summary>
        public IList Pages
        {
            get
            {
                return this.p_Pages;
            }
        }

        /// <summary>
        /// Sets the active page of the wizard to the specified index.
        /// </summary>
        /// <param name="index">The index of the page to set as the active page.</param>
        private void SetActivePage(int index)
        {
            if (index < 0 || index >= this.p_Pages.Count)
                throw new ArgumentOutOfRangeException("index");

            WizardPage page = (WizardPage)this.p_Pages[index];
            this.SetActivePage(page);
        }

        /// <summary>
        /// The function which handles when the WizardSheet is first loaded.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void WizardSheet_Load(object sender, EventArgs e)
        {
            if (this.p_Pages.Count != 0)
            {
                this.ResizeToFit();
                this.SetActivePage(0);
            }
            else
                this.SetWizardButtons(WizardButtons.None);
        }

        /// <summary>
        /// Resizes the WizardSheet so that it successfully contains all of
        /// the wizard pages.
        /// </summary>
        private void ResizeToFit()
        {
            Size max = new Size(this.c_ButtonPanel.Width, 0);

            foreach (WizardPage page in this.p_Pages)
            {
                if (page.Width > max.Width)
                    max.Width = page.Width;
                if (page.Height > max.Height)
                    max.Height = page.Height;
            }

            foreach (WizardPage page in this.p_Pages)
            {
                page.Size = max;
            }

            Size extraSize = this.Size;
            extraSize -= this.c_PagePanel.Size;

            Size newSize = max + extraSize;
            this.Size = newSize;
        }

        /// <summary>
        /// Finds a WizardPage in the Pages property by the name of the
        /// WizardPage.  Returns null if no page can be found.
        /// </summary>
        /// <param name="name">The name of the page.</param>
        /// <returns>The WizardPage object, or null if no page was found.</returns>
        private WizardPage FindPage(string name)
        {
            foreach (WizardPage page in this.p_Pages)
            {
                if (page.Name == name)
                    return page;
            }

            return null;
        }

        /// <summary>
        /// Sets the active page by the specified page name.
        /// </summary>
        /// <param name="name">The name of the page.</param>
        private void SetActivePage(string name)
        {
            WizardPage page = FindPage(name);

            if (page == null)
                throw new Exception(string.Format("Can't find page named {0}", name));

            this.SetActivePage(page);
        }

        /// <summary>
        /// Sets the active page to the specified WizardPage object.
        /// </summary>
        /// <param name="page">The WizardPage object.</param>
        private void SetActivePage(WizardPage page)
        {
            WizardPage old = this.p_ActivePage;

            // If this page isn't in the Controls collection, add it.
            // This is what causes the Load event, so we defer
            // it as late as possible.
            if (!this.c_PagePanel.Controls.Contains(page))
                this.c_PagePanel.Controls.Add(page);

            // Show this page.
            page.Visible = true;

            this.p_ActivePage = page;

            // Allow the page to cancel this.
            CancelEventArgs e = new CancelEventArgs();
            page.OnSetActive(e);

            if (e.Cancel)
            {
                page.Visible = false;
                this.p_ActivePage = old;
            }

            // Hide all of the other pages.
            foreach (WizardPage p in this.p_Pages)
            {
                if (p != page)
                    p.Visible = false;
            }
        }

        /// <summary>
        /// Sets the UAC shield icon on a button if it's
        /// required (to be determined by caller).
        /// </summary>
        /// <param name="required">Whether to show the UAC icon.</param>
        internal void SetUACRequired(bool required)
        {
            UACShield.SetShield(this.c_NextButton, required);
            UACShield.SetShield(this.c_FinishButton, required);
        }

        /// <summary>
        /// Sets what wizard buttons are currently visible.
        /// </summary>
        /// <param name="buttons">An enumeration specifing the wizard buttons to be shown.</param>
        public void SetWizardButtons(WizardButtons buttons)
        {
            // By default, no UAC icon is shown.
            this.SetUACRequired(false);

            // The Back button is simple.
            this.c_BackButton.Enabled = ((buttons & WizardButtons.Back) != 0);

            // The Next button is a bit more complicated.
            // If we've got a Finish button, then it's disabled and hidden.
            if ((buttons & WizardButtons.Finish) != 0)
            {
                this.c_FinishButton.Visible = true;
                this.c_FinishButton.Enabled = true;

                this.c_NextButton.Visible = false;
                this.c_NextButton.Enabled = false;

                this.AcceptButton = this.c_FinishButton;
            }
            else if ((buttons & WizardButtons.Next) != 0)
            {
                this.c_FinishButton.Visible = false;
                this.c_FinishButton.Enabled = false;

                this.c_NextButton.Visible = true;
                this.c_NextButton.Enabled = true;

                this.AcceptButton = this.c_NextButton;
            }
            else
            {
                // Just disable it in case we're on a Finish page (we don't want
                // to show the Next button after disabling Finish).
                this.c_FinishButton.Enabled = false;
                this.c_NextButton.Enabled = false;
            }
        }

        /// <summary>
        /// Gets the index of the currently active page in the wizard.
        /// </summary>
        /// <returns>The index of the active page.</returns>
        public int GetActiveIndex()
        {
            WizardPage active = GetActivePage();

            for (int i = 0; i < this.p_Pages.Count; ++i)
            {
                if (active == this.p_Pages[i])
                    return i;
            }

            return -1;
        }

        /// <summary>
        /// Returns the WizardPage that is currently active in the wizard.
        /// </summary>
        /// <returns>The WizardPage that is currently active in the wizard.</returns>
        private WizardPage GetActivePage()
        {
            return this.p_ActivePage;
        }

        /// <summary>
        /// Handles changing the active page, etc.. before the page change occurs.
        /// Used when moving between pages via the Next and Back buttons.
        /// </summary>
        /// <param name="delta">The amount to shift the current page index by.</param>
        /// <returns>The event arguments to be passed to the OnWizardNext or OnWizardBack event call.</returns>
        private WizardPageEventArgs PreChangePage(int delta)
        {
            // Figure out which page is next.
            int active = this.GetActiveIndex();
            int next = active + delta;

            if (next < 0 || next >= this.p_Pages.Count)
                next = active;

            // Fill in the event args.
            WizardPage newpage = (WizardPage)this.p_Pages[next];

            WizardPageEventArgs e = new WizardPageEventArgs();
            e.NewPage = newpage.Name;
            e.Cancel = false;

            return e;
        }

        /// <summary>
        /// Handles cancellation of page changing.  Used when moving between
        /// pages via the Next and Back buttons.
        /// </summary>
        /// <param name="e">The event arguments that were passed on to the OnWizardNext or OnWizardBack event call.</param>
        private void PostChangePage(WizardPageEventArgs e)
        {
            if (!e.Cancel)
                this.SetActivePage(e.NewPage);
        }

        /// <summary>
        /// This function handles when the "Next &gt;" button is pressed by the
        /// user on the wizard form.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void c_NextButton_Click(object sender, EventArgs e)
        {
            WizardPageEventArgs wpea = this.PreChangePage(+1);
            this.p_ActivePage.OnWizardNext(wpea);
            this.PostChangePage(wpea);
        }

        public void Next()
        {
            WizardPageEventArgs wpea = this.PreChangePage(+1);
            this.p_ActivePage.OnWizardNext(wpea);
            this.PostChangePage(wpea);
        }

        /// <summary>
        /// This function handles when the "&lt; Back" button is pressed by the
        /// user on the wizard form.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void c_BackButton_Click(object sender, EventArgs e)
        {
            WizardPageEventArgs wpea = this.PreChangePage(-1);
            this.p_ActivePage.OnWizardBack(wpea);
            this.PostChangePage(wpea);
        }

        public void Back()
        {
            WizardPageEventArgs wpea = this.PreChangePage(-1);
            this.p_ActivePage.OnWizardBack(wpea);
            this.PostChangePage(wpea);
        }

        private void c_FinishButton_Click(object sender, EventArgs e)
        {
            CancelEventArgs cea = new CancelEventArgs();
            this.p_ActivePage.OnWizardFinish(cea);
            if (cea.Cancel)
                return;

            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        public void Finish()
        {
            CancelEventArgs cea = new CancelEventArgs();
            this.p_ActivePage.OnWizardFinish(cea);
            if (cea.Cancel)
                return;

            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        public bool CancelEnabled
        {
            get
            {
                return this.c_CancelButton.Enabled;
            }
            set
            {
                this.c_CancelButton.Enabled = value;
            }
        }
    }

    /// <summary>
    /// An enumeration descibing the wizard buttons that are to be shown.
    /// </summary>
    [Flags]
    public enum WizardButtons
    {
        None = 0x0000,
        Back = 0x0001,
        Next = 0x0002,
        Finish = 0x0004,
    }
}
